#include <stdlib.h>
#include <math.h>
#include <xlib.h>

/*static void printPoints (imgT * img, pixelT pixel, unsigned int rad, int x2, int y);*/
static void lineBresenham(imgT * img, int x0, int y0, int x1, int y1, colorT color);
/*static int calcDistance (unsigned int rad, int point);*/
static void rasterCircle(imgT * img, int x0, int y0, int radius, colorT color);
static int validateRect(rectT * rect, imgT * img);

void drawLine(imgT * img, pointT p0, pointT p1, colorT color)
{
	lineBresenham (img, p0.x, p0.y, p1.x, p1.y, color);
}

static void setPixelPoints (pixelT * pixel, int x, int y)
{
	pixel->point.x = x;
	pixel->point.y = y;
}



extern int width_tbl[104];
extern unsigned char * chrtbl[104];
#define CHAR_BITMAP_HEIGHT 8


void draw_char(imgT * img, char c, int x, int y, int size, colorT color) {
	
	pixelT pixel;
    int i, j, ki, kj, witdh;

    int x0 = x;
    int y0 = y;
	int width;
    
    
    if (size < 1) return;
    
	c = c - ' ';
	pixel.color = color;
	width = width_tbl[c];

    for (i = 0; i< width; i++) {
        for (j=0; j<CHAR_BITMAP_HEIGHT; j++) {
            if (chrtbl[c][i] & (1<<j)) {
            	for (ki = 0; ki < size; ki++) {
            		for (kj = 0; kj < size; kj++) {
		            	setPixelPoints (&pixel, x0+i*size+ki, y0+j*size+kj);
		    			setPixel (img, pixel);
            		}
            	}
            }
        }
    }
}

void draw_string(imgT * img, char * s, int x, int y, int size, colorT color) {
	int i =0;
	while(s[i] != 0) {
		draw_char(img, s[i], x+size*CHAR_BITMAP_HEIGHT*i, y, size, color);
		i++;	
	}
}

void draw_boxed_string(imgT * img, char * s, int x, int y, int size, \
		int width, int max_lines, colorT color) {
	int row, col;
	int i =0;
	
	if (width < 1 || max_lines < 1) return;
	
	while(s[i] != 0) {
		row = i % width;
		col = i / width;
		if (col == max_lines -1 && row == width-3) {
			while (row != width) {
				draw_char(img, '.', x+size*CHAR_BITMAP_HEIGHT*row, \
					y+size*CHAR_BITMAP_HEIGHT*col, size, color);
				row++;
			}
			return;
		}
		draw_char(img, s[i], x+size*CHAR_BITMAP_HEIGHT*row, \
				y+size*CHAR_BITMAP_HEIGHT*col, size, color);
		i++;	
	}
}




static void lineBresenham(imgT * img, int x0, int y0, int x1, int y1, colorT color)
{
 	pixelT pixel;
    int dy = y1 - y0;
    int dx = x1 - x0;
    int stepx, stepy;

    if (dy < 0)
    {
    	dy = -dy;
    	stepy = -1;
    }
    else
    	stepy = 1;

    if (dx < 0)
    {
    	dx = -dx;
    	stepx = -1;
    } else
    	stepx = 1;

    dy <<= 1;
    dx <<= 1;

    pixel.color = color;

	setPixelPoints (&pixel, x0, y0);
    setPixel (img, pixel);

    if (dx > dy)
    {
        int fraction = dy - (dx >> 1);

        while (x0 != x1)
        {
            if (fraction >= 0)
            {
                y0 += stepy;
                fraction -= dx;
            }
            x0 += stepx;
            fraction += dy;
            setPixelPoints (&pixel, x0, y0);
            setPixel (img, pixel);
        }
    }
    else
    {
        int fraction = dx - (dy >> 1);

        while (y0 != y1)
        {
            if (fraction >= 0)
            {
                x0 += stepx;
                fraction -= dy;
            }
            y0 += stepy;
            fraction += dx;
            setPixelPoints (&pixel, x0, y0);
            setPixel (img, pixel);
        }
    }
}

void drawCircle(imgT * img, unsigned int rad, pointT orig, colorT color)
{
	rasterCircle (img, orig.x+rad, orig.y+rad, rad, color);
}

static void rasterCircle(imgT * img, int x0, int y0, int radius, colorT color)
{
	int f = 1 - radius;
	int ddF_x = 1;
	int ddF_y = -2 * radius;
	int x = 0;
	int y = radius;
	pixelT pixel;

	pixel.color = color;

	setPixelPoints (&pixel, x0, y0 + radius);
	setPixel(img, pixel);
	setPixelPoints (&pixel, x0, y0 - radius);
	setPixel(img, pixel);
	setPixelPoints (&pixel, x0 + radius, y0);
	setPixel(img, pixel);
	setPixelPoints (&pixel, x0 - radius, y0);
	setPixel(img, pixel);

	while(x < y)
	{
		 /*assert(ddF_x == 2 * x + 1);
		 assert(ddF_y == -2 * y);
		 assert(f == x*x + y*y - radius*radius + 2*x - y + 1);*/

		 if(f >= 0)
		 {
			   y--;
			   ddF_y += 2;
			   f += ddF_y;
		 }

		 x++;
		 ddF_x += 2;
		 f += ddF_x;

		 setPixelPoints (&pixel, x0 + x, y0 + y);
		 setPixel(img, pixel);
		 setPixelPoints (&pixel, x0 - x, y0 + y);
		 setPixel(img, pixel);
		 setPixelPoints (&pixel, x0 + x, y0 - y);
		 setPixel(img, pixel);
		 setPixelPoints (&pixel, x0 - x, y0 - y);
		 setPixel(img, pixel);
		 setPixelPoints (&pixel, x0 + y, y0 + x);
		 setPixel(img, pixel);
		 setPixelPoints (&pixel, x0 - y, y0 + x);
		 setPixel(img, pixel);
		 setPixelPoints (&pixel, x0 + y, y0 - x);
		 setPixel(img, pixel);
		 setPixelPoints (&pixel, x0 - y, y0 - x);
		 setPixel(img, pixel);
	}
}

/* My fuckin' algorithm
void drawCircle(imgT * img, unsigned int rad, pointT orig, colorT color)
{
	int startPoint = orig.y + rad, x1, x2, y;
	pixelT pixel;

	pixel.color = color;
	for (x1 = startPoint, x2 = -rad; x2 >= 0 ; x1++, x2++)
	{
		y = calcDistance (rad, x2);
		pixel.point.x = x1;
		pixel.point.y = x1-y;
		printPoints (img, pixel, rad, abs(x2), y);
	}
}

static void printPoints (imgT * img, pixelT pixel, unsigned int rad, int x2, int y)
{
	// p1 = pixel
	pixelT  p2, p3, p4;

	// Imprime los 4 puntos que son simetricos en un circulo
	p2.point.x = pixel.point.x;
	p2.point.y = pixel.point.y+2*y;
	p3.point.x = pixel.point.x+2*x2;
	p3.point.y = pixel.point.y;
	p4.point.x = pixel.point.x+2*x2;
	p4.point.y = pixel.point.y+2*y;
	p2.color = p3.color = p4.color = pixel.color;

	setPixel (img, pixel);
	setPixel (img, p2);
	setPixel (img, p3);
	setPixel (img, p4);
}
*/
static int calcDistance (unsigned int rad, int point)
{
	return (int)floor (sqrt (rad*rad - point*point));
}

void drawSquare(imgT * img, unsigned int side, pointT orig, colorT color)
{
	/* vert1 = orig */
	pointT vert2, vert3, vert4;

	vert2.x = orig.x+side;
	vert2.y = orig.y;
	vert3.x = orig.x;
	vert3.y = orig.y+side;
	vert4.x = orig.x+side;
	vert4.y = orig.y+side;

	drawLine (img, orig, vert2, color);
	drawLine (img, orig, vert3, color);
	drawLine (img, vert2, vert4, color);
	drawLine (img, vert3, vert4, color);
}

void drawRect(imgT * img, rectT rect, colorT color)
{
	/* vert1 = rect.orig */
	pointT vert2, vert3, vert4;

	vert2.x = rect.orig.x+rect.w;
	vert2.y = rect.orig.y;
	vert3.x = rect.orig.x;
	vert3.y = rect.orig.y+rect.h;
	vert4.x = rect.orig.x+rect.w;
	vert4.y = rect.orig.y+rect.h;

	drawLine (img, rect.orig, vert2, color);
	drawLine (img, rect.orig, vert3, color);
	drawLine (img, vert2, vert4, color);
	drawLine (img, vert3, vert4, color);
}

void imgBlt(imgT * imgSrc, rectT rSrc, imgT * imgDst, rectT rDst, bltopT op)
{
	rectT r;
	pointT p;
	int pixSrcIndex, pixDstIndex;
	char * imgSrcPixels, * imgDstPixels;

	/*Check images*/
	if (imgSrc == NULL || imgDst == NULL)
		return;

	/*Do some corrections and boundary checks*/
	validateRect(&rSrc, imgSrc);
	validateRect(&rDst, imgDst);

	/*Intersect the rects*/
	r.h = rSrc.h < rDst.h? rSrc.h : rDst.h;
	r.w = rSrc.w < rDst.w? rSrc.w : rDst.w;

	imgSrcPixels = imgSrc->image;
	imgDstPixels = imgDst->image;

	for (p.y = 0; p.y<r.h; p.y++)
		for (p.x = 0; p.x<r.w; p.x++)
		{
			pixSrcIndex = 3*(rSrc.orig.x + p.x + imgSrc->w * (rSrc.orig.y + p.y));
			pixDstIndex = 3*(rDst.orig.x + p.x + imgDst->w * (rDst.orig.y + p.y));

			/*Operate*/
			switch(op)
			{
			case BLT_AND:
				imgDstPixels[pixDstIndex + 0] &= imgSrcPixels[pixSrcIndex + 0];
				imgDstPixels[pixDstIndex + 1] &= imgSrcPixels[pixSrcIndex + 1];
				imgDstPixels[pixDstIndex + 2] &= imgSrcPixels[pixSrcIndex + 2];
				break;
			case BLT_OR:
				imgDstPixels[pixDstIndex + 0] |= imgSrcPixels[pixSrcIndex + 0];
				imgDstPixels[pixDstIndex + 1] |= imgSrcPixels[pixSrcIndex + 1];
				imgDstPixels[pixDstIndex + 2] |= imgSrcPixels[pixSrcIndex + 2];
				break;
			case BLT_XOR:
				imgDstPixels[pixDstIndex + 0] ^= imgSrcPixels[pixSrcIndex + 0];
				imgDstPixels[pixDstIndex + 1] ^= imgSrcPixels[pixSrcIndex + 1];
				imgDstPixels[pixDstIndex + 2] ^= imgSrcPixels[pixSrcIndex + 2];
				break;
			default:
				imgDstPixels[pixDstIndex + 0] = imgSrcPixels[pixSrcIndex + 0];
				imgDstPixels[pixDstIndex + 1] = imgSrcPixels[pixSrcIndex + 1];
				imgDstPixels[pixDstIndex + 2] = imgSrcPixels[pixSrcIndex + 2];
			}
		}
}

static int validateRect(rectT * rect, imgT * img)
{
	if (rect->w + rect->orig.x < 0 ||
		rect->h + rect->orig.y < 0 ||
		rect->orig.x > img->w ||
		rect->orig.y > img->h)
		return 0;

	if (rect->orig.x < 0)
		rect->w += rect->orig.x;
	if (rect->orig.y < 0)
		rect->h += rect->orig.y;
	if (rect->w + rect->orig.x > img->w)
		rect->w = rect->orig.x - img->w;
	if (rect->h + rect->orig.y > img->h)
		rect->h = rect->orig.y - img->h;
	return 1;
}
